package svc

import (
	"trusteva/app/eva/internal/config"
	"trusteva/app/eva/internal/middleware"
	"trusteva/app/eva/model"

	"github.com/zeromicro/go-zero/core/stores/redis"
	"github.com/zeromicro/go-zero/rest"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

type ServiceContext struct {
	Config config.Config

	RedisClient *redis.Redis

	DB              *gorm.DB
	JwtWithRBACAuth rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	db, err := gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
			NoLowerCase:   true,
		},
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger:                                   logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		panic(err)
	}

	// 自动同步表结构
	if !c.Release {
		sync(db)
	}

	rds := redis.New(c.Redis.Host, func(r *redis.Redis) {
		r.Type = c.Redis.Type
		r.Pass = c.Redis.Pass
	})

	// 同步规则\app\whitelist到RDS
	if err := syncRules(db, rds); err != nil {
		panic(err)
	}
	if err := syncApps(db, rds); err != nil {
		panic(err)
	}
	if err := syncWhiteList(db, rds); err != nil {
		panic(err)
	}

	return &ServiceContext{
		Config:          c,
		RedisClient:     rds,
		DB:              db,
		JwtWithRBACAuth: middleware.NewJwtWithRBACAuthMiddleware(db, c.JwtAuth.AccessSecret, c.JwtAuth.AccessExpire).Handle,
	}
}

func sync(db *gorm.DB) {
	db.AutoMigrate(&model.App{})
	db.AutoMigrate(&model.Rule{})
	db.AutoMigrate(&model.Whitelist{})
	db.AutoMigrate(&model.Warning{})
}

func syncRules(db *gorm.DB, rds *redis.Redis) error {
	// 删除key
	if _, err := rds.Del(model.RuleSetKey); err != nil {
		return err
	}
	var (
		page = 1
		size = 1000
	)
	for {
		var ms []model.Rule
		tx := db.Where("Status=1").Limit(size).Offset((page - 1) * size).Find(&ms)
		if tx.Error != nil {
			return tx.Error
		}

		if tx.RowsAffected > 0 {
			page += 1
			keys := make([]any, 0)
			for _, rule := range ms {
				if rule.Status == model.RuleBlock {
					continue
				}
				keys = append(keys, rule.Key())

			}
			if len(keys) > 0 {
				if _, err := rds.Sadd(model.RuleSetKey, keys...); err != nil {
					return err
				}
			}

		}

		if tx.RowsAffected < int64(size) {
			break
		}
	}

	return nil
}

func syncApps(db *gorm.DB, rds *redis.Redis) error {
	keys, err := rds.Keys("app_*")
	if err != nil {
		return err
	}
	for _, v := range keys {
		if _, err := rds.Del(v); err != nil {
			return err
		}
	}

	var apps []model.App
	if err := db.Where("1=1").Find(&apps).Error; err != nil {
		return err
	}
	for _, v := range apps {
		if err := rds.Set(v.Key(), v.Value()); err != nil {
			return err
		}
	}
	return nil
}

func syncWhiteList(db *gorm.DB, rds *redis.Redis) error {
	keys, err := rds.Keys("whitelist_*")
	if err != nil {
		return err
	}
	for _, v := range keys {
		if _, err := rds.Del(v); err != nil {
			return err
		}
	}

	var apps []model.Whitelist
	if err := db.Where("1=1").Find(&apps).Error; err != nil {
		return err
	}
	for _, v := range apps {
		if err := rds.Set(v.Key(), v.Value()); err != nil {
			return err
		}
	}
	return nil
}
